import { describe, getTime } from '$lib';
import { registerAction } from '$lib/actions';
import { Clock, registerOnTick, registerOnTickActive } from '$lib/clock';
import { Description, Feature, Glyph, Interesting } from '$lib/components';
import { distance, getPos2, landscapeAt, onTile } from '$lib/pos2';
import { log } from '$lib/log';
import { Types, addComponent, defineComponent, defineQuery, hasComponent } from '$lib/ecs';

export const Elevation = defineComponent('Elevation', {
  level: Types.ui16,
});

export const Flooded = defineComponent('Flooded', {
  level: Types.ui16,
});

export const Spring = defineComponent('Spring', {
  amount: Types.ui16,
});

export const Drain = defineComponent('Drain', {
  amount: Types.ui16,
});

export const Drainage = defineComponent('Drainage', {
  amount: Types.ui16,
});

export const springQuery = defineQuery([Spring, Clock]);
export const floodedQuery = defineQuery([Flooded, Clock]);
export const drainageQuery = defineQuery([Drainage]);

export const spring = [
  [Feature],
  [Glyph, {
    glyph: '*',
    color: '#4bf',
  }],
  [Description, {
    summary: 'spring',
    description: "Cool water spring. Must be very refreshing.",
  }],
  [Spring, {
    amount: 10,
  }],
  [Interesting],
  [Clock],
];

export const drain = [
  [Feature],
  [Glyph, {
    glyph: ';',
    color: '#852',
  }],
  [Description, {
    summary: 'drain',
    description: "It takes the water away.",
  }],
  [Drainage, {
    amount: 100,
  }],
];

const flood = (world, x, y, amount) => {
  const e = landscapeAt(world, x, y);
  if (e === undefined)
    return;
  if (hasComponent(world, Drain, e)) {
    amount -= Drain.amount[e];
    if (amount <= 0)
      return;
  }
  if (!hasComponent(world, Flooded, e)) {
    addComponent(world, Flooded, e);
  }
  if (!hasComponent(world, Clock, e)) {
    Clock.created[e] = getTime(world);
    addComponent(world, Clock, e);
    Clock.time[e] = getTime(world) + 5;
  }
  Flooded.level[e] += amount;
};

const unflood = (world, x, y, amount) => {
  const e = landscapeAt(world, x, y);
  if (e === undefined)
    return;
  if (!hasComponent(world, Flooded, e))
    return;
  if (Flooded.level[e] > 0)
    Flooded.level[e] = Math.max(Flooded.level[e] - amount, 0);
};

export const waterLevel = (world, e) => {
  return (Flooded.level[e] ?? 0) + (Elevation.level[e] ?? 0);
};

export const waterLevelAt = (world, x, y) => {
  const e = landscapeAt(world, x, y);
  if (e === undefined)
    return Infinity;
  return waterLevel(world, e);
};

const adjacent = [[-1, -1], [0, -1], [1, -1], [-1, 0], [1, 0], [-1, 1], [0, 1], [1, 1]];

registerOnTickActive(Flooded, (world, flooded) => {
  const { x, y } = getPos2(flooded);
  const water = Flooded.level[flooded];
  const level = waterLevel(world, flooded);
  let wantSpill = 0;
  for (let [dx, dy] of adjacent) {
    const otherLevel = waterLevelAt(world, x + dx, y + dy);
    wantSpill += Math.max(level-otherLevel, 0);
  }
  const spill = Math.min(water, Math.floor(wantSpill / 2));
  let spilled = 0;
  if (spill > 0) {
    for (let [dx, dy] of adjacent) {
      const otherLevel = waterLevelAt(world, x + dx, y + dy);
      const n = Math.max(Math.floor((level-otherLevel)/wantSpill*spill), 0);
      if (n > 0) {
        flood(world, x+dx, y+dy, n);
        spilled += n;
      }
    }
  }
  Flooded.level[flooded] -= spilled;
  Clock.time[flooded] += 5;
});

registerOnTickActive(Spring, (world, spring) => {
  const { x, y } = getPos2(spring);
  flood(world, x, y, Spring.amount[spring]);
  Clock.time[spring] += 5;
});

export const DrinkFrom = (source) => (world, e) => {
  return {
    error: distance(getPos2(e), getPos2(source)) > 1 && "you can't drink from non-adjacent source",
    action: () => log(`${describe(world, e)} drinks from ${describe(world, source)}.`),
  };
};

registerAction([Spring], 'drink', DrinkFrom);
